package com.suxin.thread.future;

import java.util.LinkedList;
import java.util.Queue;

/**
 * @author Tang
 * @classname ListenableFutureTaskCallbackRegister
 * @description [  ]
 * @date 2023/2/7 09:53
 */
public class ListenableFutureTaskCallbackRegister<T> {

    private final Queue<SuccessCallback<? super T>> successCallbackQueue = new LinkedList<>();
    private final Queue<FailureCallback> failureCallbackQueue = new LinkedList<>();

    private State state = State.NEW;

    private final Object mutex = new Object();

    private Object result;

    public void addCallback(ListenableFutureCallback<T> callback) {
        synchronized (mutex) {
            switch (state) {
                case NEW:
                    successCallbackQueue.add(callback);
                    failureCallbackQueue.add(callback);
                    break;
                case FAILURE:
                    notifyFailure(callback);
                    break;
                case SUCCESS:
                    notifySuccess(callback);
                    break;
                default:
                    break;
            }
        }
    }

    public void addSuccessCallback(SuccessCallback<T> callback) {
        synchronized (mutex) {
            switch (state) {
                case NEW:
                    successCallbackQueue.add(callback);
                    break;
                case SUCCESS:
                    notifySuccess(callback);
                    break;
                default:
                    break;
            }
        }
    }

    public void addFailureCallback(FailureCallback callback) {
        synchronized (mutex) {
            switch (state) {
                case NEW:
                    failureCallbackQueue.add(callback);
                    break;
                case FAILURE:
                    notifyFailure(callback);
                    break;
                default:
                    break;
            }
        }
    }

    public void notifySuccess(SuccessCallback<? super T> callback) {
        callback.onSuccess((T)this.result);
    }

    public void notifyFailure(FailureCallback callback) {
        callback.onFailure((Throwable)this.result);
    }

    public void success(T v) {
        this.result = v;
        this.state = State.SUCCESS;
        SuccessCallback<? super T> callback;
        while ((callback = successCallbackQueue.poll()) != null) {
            notifySuccess(callback);
        }
    }

    public void failure(Throwable v) {
        this.result = v;
        this.state = State.FAILURE;
        FailureCallback callback;
        while ((callback = failureCallbackQueue.poll()) != null) {
            notifyFailure(callback);
        }
    }

    private enum State {
        /**
         * new
         */
        NEW,SUCCESS,FAILURE
    }

}
